/*
 * \brief  Thread facility
 * \author Christian Helmuth
 * \date   2008-10-20
 *
 */

/*
 * Copyright (C) 2008-2009 Christian Helmuth
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#include <base/env.h>
#include <base/sleep.h>
#include <base/lock.h>

extern "C" {
#include <dde_kit/thread.h>
#include <dde_kit/timer.h>
}

#include "thread.h"

using namespace Genode;


/**********+*********
 ** Stack registry **
 ********************/

class Stack : public Avl_node<Stack>
{
	public:

		class Not_found : public Exception { };

	private:

		void            *_stack;
		size_t           _size;

		Dde_kit::Thread *_thread;

	public:

		Stack(void *stack, size_t size, Dde_kit::Thread *thread)
		: _stack(stack), _size(size), _thread(thread) { }

		/** AVL node comparison */
		bool higher(Stack *stack) {
			return ((char *)_stack + _size <= stack->_stack); }

		/** AVL node lookup */
		Stack *lookup(void *stack_pointer)
		{
			if (stack_pointer >= _stack &&
			    stack_pointer < (char *)_stack + _size)
				return this;

			Stack *s = child(_stack < stack_pointer);

			if (!s) throw Not_found();

			return s->lookup(stack_pointer);
		}

		Dde_kit::Thread *thread() const { return _thread; }
};


class Stack_database : public Avl_tree<Stack>
{
	private:

		Lock _lock;

	public:

		Stack *lookup(void *stack_pointer)
		{
			Lock::Guard lock_guard(_lock);

			if (!first()) throw Stack::Not_found();

			return first()->lookup(stack_pointer);
		}

		void insert(Stack *stack)
		{
			Lock::Guard lock_guard(_lock);

			Avl_tree<Stack>::insert(stack);
		}

		void remove(Stack *stack)
		{
			Lock::Guard lock_guard(_lock);

			stack->remove();
		}
};


static Stack_database *stacks()
{
	static Stack_database _stacks;
	return &_stacks;
}


/********************
 ** Generic thread **
 ********************/

enum { STACK_SIZE = 0x2000 };

unsigned Dde_kit::Thread::_id_counter = 0x1000;


void Dde_kit::Thread::start()
{
	_start((char *)_stack + STACK_SIZE - sizeof(umword_t), _name);
}


Dde_kit::Thread::Thread(const char *name) : _name(name), _id(_id_counter++)
{
	/* allocate stack */
	_stack = env()->heap()->alloc(STACK_SIZE);

	/* register stack region */
	Stack *stack = new (env()->heap()) Stack(_stack, STACK_SIZE, this);
	stacks()->insert(stack);
}


Dde_kit::Thread::~Thread()
{
	/* unregister stack */
	try {
		stacks()->remove(stacks()->lookup(_stack));
	} catch (...) { }

	/* free stack */
	env()->heap()->free(_stack, STACK_SIZE);
}


/********************
 ** DDE Kit thread **
 ********************/

struct dde_kit_thread { };

class _Thread : Dde_kit::Thread
{
	private:

		void (*_thread_fn)(void *);
		void  *_thread_arg;

	public:

		_Thread(const char *name, void (*thread_fn)(void *), void *thread_arg)
		:
			Dde_kit::Thread(name),
			_thread_fn(thread_fn), _thread_arg(thread_arg) {
			start();
		}

		void entry() { _thread_fn(_thread_arg); }
};


extern "C" struct dde_kit_thread *dde_kit_thread_create(void (*fun)(void *),
                                                        void *arg, const char *name)
{
	try {
		return reinterpret_cast<struct dde_kit_thread *>
		       (new (env()->heap()) _Thread(name, fun, arg));
	} catch (...) {
		PERR("thread creation failed");
		return 0;
	}
}


extern "C" struct dde_kit_thread *dde_kit_thread_myself()
{
	int dummy;

	try {
		return reinterpret_cast<struct dde_kit_thread *>(stacks()->lookup(&dummy)->thread());
	} catch (...) {
		return 0;
	}
}


extern "C" void * dde_kit_thread_get_data(struct dde_kit_thread *thread) {
	return reinterpret_cast<Dde_kit::Thread *>(thread)->data(); }


extern "C" void * dde_kit_thread_get_my_data(void)
{
	int dummy;

	try {
		return (stacks()->lookup(&dummy))->thread()->data();
	} catch (...) {
		PERR("current thread not in stack database");
		return 0;
	}
}


extern "C" void dde_kit_thread_set_data(struct dde_kit_thread *thread, void *data) {
	reinterpret_cast<Dde_kit::Thread *>(thread)->data(data); }


extern "C" void dde_kit_thread_set_my_data(void *data)
{
	int dummy;

	try {
		(stacks()->lookup(&dummy))->thread()->data(data);
	} catch (...) {
		PERR("current thread not in stack database");
	}
}


extern "C" void dde_kit_thread_exit(void)
{
	PERR("not implemented yet");

	sleep_forever();
}


extern "C" const char *dde_kit_thread_get_name(struct dde_kit_thread *thread) {
	return reinterpret_cast<Dde_kit::Thread *>(thread)->name(); }


extern "C" int dde_kit_thread_get_id(struct dde_kit_thread *thread) {
	return reinterpret_cast<Dde_kit::Thread *>(thread)->id(); }


extern "C" void dde_kit_thread_schedule(void)
{
	PERR("not implemented yet");
}


/*********************
 ** Sleep interface **
 *********************/

static void _wake_up_msleep(void *lock)
{
	reinterpret_cast<Lock *>(lock)->unlock();
}


extern "C" void dde_kit_thread_msleep(unsigned long msecs)
{
	/*
	 * This simple msleep() registers a timer that fires after 'msecs' and
	 * blocks on 'lock'. The registered timer handler just unlocks 'lock' and
	 * the sleeping thread unblocks.
	 */
	Lock *lock;
	struct dde_kit_timer *timer;

	unsigned long timeout = jiffies + (msecs * DDE_KIT_HZ) / 1000;

	lock = new (env()->heap()) Lock(Lock::LOCKED);
	timer = dde_kit_timer_add(_wake_up_msleep, lock, timeout);

	lock->lock();

	destroy((env()->heap()), lock);
}


extern "C" void dde_kit_thread_usleep(unsigned long usecs)
{
	dde_kit_thread_schedule();
}


extern "C" void dde_kit_thread_nsleep(unsigned long nsecs)
{
	dde_kit_thread_schedule();
}


extern "C" void dde_kit_init_threads(void) { }

